﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace CounterStrikeSourceAimbot.MapUi
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this.Context = new MainWindowDataContext();

            this.Context.IsLocalServerChanged += (s, e) =>
            {
                if (Context.AimBot != null)
                    (s as MainWindowDataContext).AimBot.ValidatePosition = !e.NewValue;
            };

            this.Context.AutoAimOnHighlightsOnlyChanged += (s, e) =>
            {
                if (Context.AimBot != null)
                    (s as MainWindowDataContext).AimBot.OnlyHighlightedUsers = e.NewValue;
            };

            this.Activated += OnActivated;

            var tmr = new Timer();

            tmr.Interval = 100;

            tmr.Elapsed += (sender, args) => this.Dispatcher.BeginInvoke(
                DispatcherPriority.Background,
                new Action(() =>Guid.NewGuid())); ;

            this.Closing += (sender, args) => Application.Current.Shutdown();
        }


        private void SetTopmost()
        {
            var handle = new WindowInteropHelper(this).Handle;
            SetForegroundWindow(handle);
        }

        private void OnActivated(object sender, EventArgs eventArgs)
        {
           //SetTopmost();
        }


        public MainWindowDataContext Context;

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                var res = this.Context.StartRestart();

                if (!string.IsNullOrEmpty(res))
                {
                    MessageBox.Show(res, "Map - Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                else
                {
                    MessageBox.Show("Successfully Started");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error");
            }

        }

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
                this.DragMove();
        }

        private void ExitMenuItem_OnClick(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void HelpMenuItem_OnClick(object sender, RoutedEventArgs e)
        {
            System.Diagnostics.Process.Start(@"https://cssaimbot.codeplex.com/");
        }

        private void ButtonHl_Click(object sender, RoutedEventArgs e)
        {
            var wnd = new HighlightSelector();

            wnd.DataContext = Context.AvailablePlayers;
            //wnd.DataGrid.CancelEdit();
            wnd.ShowDialog();
        }

        private void ButtonAb_Click(object sender, RoutedEventArgs e)
        {
            var aim = Context.AimBot = new AimBot(Context.AvailablePlayers.ToList(),
                Context.AvailablePlayers.First(i => i.Name == Context.PlayerName));
            
            aim.BindAsync();

        }
    }

    public class MainWindowDataContext : INotifyPropertyChanged
    {
        public MainWindowDataContext()
        {
            this.PlayerName = "epsi1on";
        }

        #region INotifyPropertyChanged members and helpers

        public event PropertyChangedEventHandler PropertyChanged;

        private static bool AreEqualObjects(object obj1, object obj2)
        {
            var obj1Null = ReferenceEquals(obj1, null);
            var obj2Null = ReferenceEquals(obj2, null);

            if (obj1Null && obj2Null)
                return true;

            if (obj1Null || obj2Null)
                return false;

            if (obj1.GetType() != obj2.GetType())
                return false;

            if (ReferenceEquals(obj1, obj2))
                return true;

            return obj1.Equals(obj2);
        }

        private void OnPropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null)
                return;

            if (this.PropertyChanged != null)
                foreach (var propertyName in propertyNames)
                    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region PlayerName Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public string PlayerName
        {
            get { return playerName; }
            set
            {
                if (AreEqualObjects(playerName, value))
                    return;

                var _fieldOldValue = playerName;

                playerName = value;

                MainWindowDataContext.OnPlayerNameChanged(this, new PropertyValueChangedEventArgs<string>(_fieldOldValue, value));

                this.OnPropertyChanged("PlayerName");
            }
        }

        private string playerName;

        public EventHandler<PropertyValueChangedEventArgs<string>> PlayerNameChanged;

        public static void OnPlayerNameChanged(object sender, PropertyValueChangedEventArgs<string> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.PlayerNameChanged != null)
                obj.PlayerNameChanged(obj, e);


        }

        #endregion

        #region Engine Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public MemoryEngine Engine
        {
            get { return engine; }
            set
            {
                if (AreEqualObjects(engine, value))
                    return;

                var _fieldOldValue = engine;

                engine = value;

                MainWindowDataContext.OnEngineChanged(this,
                    new PropertyValueChangedEventArgs<MemoryEngine>(_fieldOldValue, value));

                this.OnPropertyChanged("Engine");
            }
        }

        private MemoryEngine engine;

        public EventHandler<PropertyValueChangedEventArgs<MemoryEngine>> EngineChanged;

        public static void OnEngineChanged(object sender, PropertyValueChangedEventArgs<MemoryEngine> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.EngineChanged != null)
                obj.EngineChanged(obj, e);
        }

        #endregion

        #region AvailablePlayers Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public ObservableCollection<PlayerData> AvailablePlayers
        {
            get { return availablePlayers; }
            set
            {
                if (AreEqualObjects(availablePlayers, value))
                    return;

                var _fieldOldValue = availablePlayers;

                availablePlayers = value;

                MainWindowDataContext.OnAvailablePlayersChanged(this,
                    new PropertyValueChangedEventArgs<ObservableCollection<PlayerData>>(_fieldOldValue, value));

                this.OnPropertyChanged("AvailablePlayers");
            }
        }

        private ObservableCollection<PlayerData> availablePlayers;

        public EventHandler<PropertyValueChangedEventArgs<ObservableCollection<PlayerData>>> AvailablePlayersChanged;

        public static void OnAvailablePlayersChanged(object sender,
            PropertyValueChangedEventArgs<ObservableCollection<PlayerData>> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.AvailablePlayersChanged != null)
                obj.AvailablePlayersChanged(obj, e);
        }

        #endregion

        #region WorldData Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public WorldData WorldData
        {
            get { return worldData; }
            set
            {
                if (AreEqualObjects(worldData, value))
                    return;

                var _fieldOldValue = worldData;

                worldData = value;

                MainWindowDataContext.OnWorldDataChanged(this, new PropertyValueChangedEventArgs<WorldData>(_fieldOldValue, value));

                this.OnPropertyChanged("WorldData");
            }
        }

        private WorldData worldData;

        public EventHandler<PropertyValueChangedEventArgs<WorldData>> WorldDataChanged;

        public static void OnWorldDataChanged(object sender, PropertyValueChangedEventArgs<WorldData> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.WorldDataChanged != null)
                obj.WorldDataChanged(obj, e);


        }

        #endregion

        #region RefreshInterval Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public int RefreshInterval
        {
            get { return refreshInterval; }
            set
            {
                if (AreEqualObjects(refreshInterval, value))
                    return;

                var _fieldOldValue = refreshInterval;

                refreshInterval = value;

                MainWindowDataContext.OnRefreshIntervalChanged(this,
                    new PropertyValueChangedEventArgs<int>(_fieldOldValue, value));

                this.OnPropertyChanged("RefreshInterval");
            }
        }

        private int refreshInterval;

        public EventHandler<PropertyValueChangedEventArgs<int>> RefreshIntervalChanged;

        public static void OnRefreshIntervalChanged(object sender, PropertyValueChangedEventArgs<int> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.RefreshIntervalChanged != null)
                obj.RefreshIntervalChanged(obj, e);
        }

        #endregion

        #region IsLocalServer Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public bool IsLocalServer
        {
            get { return isLocalServer; }
            set
            {
                if (AreEqualObjects(isLocalServer, value))
                    return;

                var _fieldOldValue = isLocalServer;

                isLocalServer = value;

                MainWindowDataContext.OnIsLocalServerChanged(this, new PropertyValueChangedEventArgs<bool>(_fieldOldValue, value));

                this.OnPropertyChanged("IsLocalServer");
            }
        }

        private bool isLocalServer = true;

        public EventHandler<PropertyValueChangedEventArgs<bool>> IsLocalServerChanged;

        public static void OnIsLocalServerChanged(object sender, PropertyValueChangedEventArgs<bool> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.IsLocalServerChanged != null)
                obj.IsLocalServerChanged(obj, e);


        }

        #endregion

        #region AimBot Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public AimBot AimBot
        {
            get { return aimBot; }
            set
            {
                if (AreEqualObjects(aimBot, value))
                    return;

                var _fieldOldValue = aimBot;

                aimBot = value;

                MainWindowDataContext.OnAimBotChanged(this, new PropertyValueChangedEventArgs<AimBot>(_fieldOldValue, value));

                this.OnPropertyChanged("AimBot");
            }
        }

        private AimBot aimBot;

        public EventHandler<PropertyValueChangedEventArgs<AimBot>> AimBotChanged;

        public static void OnAimBotChanged(object sender, PropertyValueChangedEventArgs<AimBot> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.AimBotChanged != null)
                obj.AimBotChanged(obj, e);


        }

        #endregion

        #region AutoAimOnHighlightsOnly Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public bool AutoAimOnHighlightsOnly
        {
            get { return autoAimOnHighlightsOnly; }
            set
            {
                if (AreEqualObjects(autoAimOnHighlightsOnly, value))
                    return;

                var _fieldOldValue = autoAimOnHighlightsOnly;

                autoAimOnHighlightsOnly = value;

                MainWindowDataContext.OnAutoAimOnHighlightsOnlyChanged(this, new PropertyValueChangedEventArgs<bool>(_fieldOldValue, value));

                this.OnPropertyChanged("AutoAimOnHighlightsOnly");
            }
        }

        private bool autoAimOnHighlightsOnly;

        public EventHandler<PropertyValueChangedEventArgs<bool>> AutoAimOnHighlightsOnlyChanged;

        public static void OnAutoAimOnHighlightsOnlyChanged(object sender, PropertyValueChangedEventArgs<bool> e)
        {
            var obj = sender as MainWindowDataContext;

            if (obj.AutoAimOnHighlightsOnlyChanged != null)
                obj.AutoAimOnHighlightsOnlyChanged(obj, e);


        }

        #endregion

        public string StartRestart()
        {
            var prc = System.Diagnostics.Process.GetProcessesByName("hl2");

            if (prc.Length == 0)
            {
                return "hl2.exe not found!";
            }

            if (prc.Length > 1)
            {
                return "multiple hl2.exe found!";
            }

            Engine = new MemoryEngine(prc.First());
            engine.WorldData = this.WorldData = new WorldData();
            engine.MainPlayerName = this.PlayerName;

            int cnt;

            var address = engine.FindPlayerListAddress(this.PlayerName, out cnt);

            //var firstAddress = engine.GetStartingPlayer(address);

            //var cnt = engine.GetPlayerCount(address);


            if (address == -1)
                return "Unable to find offsets! make sure at least two active players are in the game";

            engine.PlayerListStartAddress = address;

            this.AvailablePlayers =
                new ObservableCollection<PlayerData>(
                    new List<PlayerData>(Enumerable.Repeat(0, cnt).Select(i => new PlayerData())));

            
            var tmr = new Timer(RefreshInterval == 0 ? 100 : RefreshInterval);

            tmr.Elapsed += TmrOnElapsed;

            tmr.Start();

            return null;
        }

        

        public void ExpandPlayers()
        {
            //check for new players and add them to list

        }


        private void TmrOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            RefreshPlayerData();
        }

        private void RefreshPlayerData()
        {
            engine.RefreshClients(availablePlayers.ToArray());
        }
    }
}
